#! /usr/bin/env python

import socket
import serverinfo
import json
from utility import *
import prime
import asyncore

import logging

logger = logging.getLogger(__file__)
logger.setLevel(logging.INFO)
console = logging.StreamHandler()
logger.addHandler(console)

class DLHandler(asyncore.dispatcher_with_send):#
	'''
	Aufgaben dieser Klasse sind:

		* Server bestimmt :math:`g, h, p` wobei	:math:`p` ist Primzahl
		  mit der Eigenschaft :math:`q = (p-1)/2` auch Primzahl ist; wird ueber GP gesucht.
		* Server sendet :math:`g,h,p` beim ersten Kontakt an Client.
		* erhaelt von Client eine Tupel mit :math:`y, k, l`; :math:`y = g^k \cdot h^l`; sucht Kollision.
		* wenn Kollision gefunden, dann berechne mit chinesischem Restsatz (Pari GP) den Wert x, sodass
		  :math:`g^x = h \mod p`.
		* schickt Bestaetigung an Client beim naechsten Kontakt, dass Kollision gefunden wurde.

	'''

	def __init__(self,sock, pollardSeries, parentSocket):#
		'''
			Konstruktor

			:param sock: Der Socket, der mit Client verbindet. Instances dieser Klasse werden
				ueber diesen Socket mit dem Client kommunizieren.
			:param pollardSeries: Der gemeinsame Dictionary, in der werden die distinguish Points
				aus unterschiedlichen gespeichert.
			:parentSocket: Der Socket, der diese Klasse ruft um neue Instance zu erzeugen.
		'''
		asyncore.dispatcher_with_send.__init__(self,sock)
		
		self.receivedValues = pollardSeries
		self.parentSocket = parentSocket
		
		self.q = prime.q
		self.p = prime.p
		self.g = searchFirstErzeuger(self.p, [[2,1], [self.q,1]])
		self.h = 13
		self.kollisionGefunden = False
		
	def handle_read(self):#
		'''
		liest die Daten aus Client
		'''
		self.data = self.recv(1024)
		if self.data:
			if(self.kollisionGefunden):
				# print "confirmed"
				self.sendConfirm()
			else:
				try:
					r = self.parseData(self.data)
					if(r is None):
						self.sendParameter()
					else:
						collision = self.computeCollision(r)
						
						if(collision == True):
							self.kollisionGefunden = True
							self.sendConfirm()
						else:
							self.save(r)
							self.sendNotCollision()
				except ValueError as ex:
					logger.error( "++++ cannot parse this string:" )
					logger.error( ">>>>%s<<<<" %self.data )
	
	def sendNotCollision(self):#
		'''
			sendet ``{"Confirm":false }`` an Client.
		'''
		notCollision = '{"Confirm":false }' 
		#print "send to client"
		#print notCollision
		self.send(notCollision)
		

			
	def parseData(self, data):#
		'''
		parsert Daten von Client.

		:param data: Json String vom Server.
		'''
		job = json.loads(data)
		value = job.get("FirstContact")
		if(value == True):
			return None
		elif(value == None):
			value = job.get("Point")
			y = value["y"]
			k = value["k"]
			l = value["l"]
			
			return (y,k,l)
			
	
	def save(self, r):#
		'''
			speichert die vom Client gesendeten Werte in einer Hashtabelle.
			:param r: Eine Tupel ``(y,(k,l))``
		'''
		self.receivedValues.update( [[ r[0], ( r[1], r[2] ) ]] )
		logger.debug( "l of dic: %d" %len(self.receivedValues) )

			
			
	def sendParameter(self):#
		'''
			sendet nach dem FirstContact die Parameter g,h,p
			an Client
			``{"DLProblem": {"g":<value>, "h":<value>, "p":<value> }}``
		'''
		DLProblem = '{"DLProblem": {"g":%d, "h": %d, "p": %d} }' %(self.g, self.h, self.p)
		logger.info( "send to client %s" %DLProblem )
		self.send(DLProblem)
		
		
	
	def computeCollision(self,r):#
		'''
			sucht in hashtabelle, ob Kollision gefunden wurde. wenn nicht, return ``False``.
			Wenn gefunden, return ``True``.

			:param r: eine Tupel ``(y,(k,l))``
		'''
		if (self.receivedValues.has_key(r[0])):
			#k_i = self.receivedValues [r[0]][1]
			#l_i = self.receivedValues [r[0]][1]
			k_i = self.receivedValues [r[0]][0]
			l_i = self.receivedValues [r[0]][1]
			
			#k_j = r[0]
			#l_j = r[1]
			
			k_j = r[1]
			l_j = r[2]
			
						
			x = calculateDLFromCollision(k_i, l_i, k_j, l_j, self.q)
			logger.debug( "Point 1 k_i = %d, l_i = %d" %(k_i, l_i) )
			logger.debug( "Point 2 k_j = %d, l_j = %d" %(k_j, l_j) )

			if(x is None):
				return False
			else:
				
				DLProblem = "DL Problem: g=%d h=%d p=%d "%(self.g,self.h,self.p)
				DLSolve = "x=%d\n"%x
				self.x = x
				logger.info( DLProblem + DLSolve )
				resultFile = file("./result.txt","a")
				resultFile.write(DLProblem)
				resultFile.write(DLSolve)
				return True
	
	def sendConfirm(self):#
		'''
			falls Kollision entdeckt, sende Confirm an Client.
			``{"Confirm":true}``
		'''
		confirm = '{"Confirm":true}'
		self.send(confirm)
		self.close()
		self.socket.close()# force close connection
		self.parentSocket.close()
		

class Server(asyncore.dispatcher):#
	'''
	empfeangt eine neue Kontakt und leitet weiter an eine neue Instance
	der Klasse :py:class:`~pythoncrypt.pollardLambdaAnsync.DLHandler`
	'''
	def __init__(self, host, port):

		asyncore.dispatcher.__init__(self)
		self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
		self.set_reuse_addr()
		self.bind((host, port))
		self.listen(5)
		self.pollardSeries = {}
		print "port open"
		
	def handle_accept(self):#
		'''
		akzeptet die Kontakt eines Client, erzeugt eine neue Instance der Klasse
		``DLHandler`` und leitet die Verbindung an der neu erzeugten Instance weiter.
		'''
		pair = self.accept()
		if pair is None:
			pass
		else:
			sock, addr = pair
			# print 'Incoming connection from %s' % repr(addr)
			handler = DLHandler(sock,self.pollardSeries,self)

if __name__ == "__main__":
	host = serverinfo.HOST
	port = serverinfo.PORT
	server = Server(host, port)
	
	asyncore.loop()
	
